package com.medusa.aps.business.modules.basic.mp.service.impl;


import cn.hutool.core.builder.EqualsBuilder;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.medusa.aps.business.model.constant.RedisConstants;
import com.medusa.aps.business.model.pagination.Page;
import com.medusa.aps.business.modules.basic.model.input.MaterialUnitDto;
import com.medusa.aps.business.modules.basic.model.input.MaterialUnitParam;
import com.medusa.aps.business.modules.basic.mp.entity.Material;
import com.medusa.aps.business.modules.basic.mp.entity.MaterialUnit;
import com.medusa.aps.business.modules.basic.mp.mapper.MaterialUnitMapper;
import com.medusa.aps.business.modules.basic.mp.service.MaterialService;
import com.medusa.aps.business.modules.basic.mp.service.MaterialUnitService;
import com.medusa.aps.business.common.model.resp.SystemCode;
import com.medusa.aps.business.common.redis.annotation.Redisson;
import com.medusa.aps.business.global.model.exception.GlobalException;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 物料单位服务实现类
 *
 * @author yuxh
 * @since 2024-02-03 16:20:25
 */
@Service("materialUnitService")
public class MaterialUnitServiceImpl extends ServiceImpl<MaterialUnitMapper, MaterialUnit> implements MaterialUnitService {

    @Resource
    private MaterialService materialService;


    @Override
    public Page<MaterialUnit> selectAll(MaterialUnitParam materialUnitParam) {
        return this.lambdaQuery().select(MaterialUnit::getId, MaterialUnit::getProductUnit, MaterialUnit::getPurchaseUnit, MaterialUnit::getProductPurchaseUnitRatio)
                .like(StringUtils.hasLength(materialUnitParam.getProductUnit()), MaterialUnit::getProductUnit, materialUnitParam.getProductUnit())
                .like(StringUtils.hasLength(materialUnitParam.getPurchaseUnit()), MaterialUnit::getPurchaseUnit, materialUnitParam.getPurchaseUnit())
                .orderByDesc(MaterialUnit::getCreateTime)
                .page(materialUnitParam);
    }

    /**
     * 保存物料分类
     *
     * @param materialUnitDtos 物料单位dto集合
     */
    @Override
    @Redisson(name = RedisConstants.MATERIAL_UNIT_LOCK_KEY, waitTime = 1)
    public void saveMaterialUnit(List<MaterialUnitDto> materialUnitDtos) {
        validationParam(materialUnitDtos);
        List<MaterialUnit> materialUnits = materialUnitDtos.stream()
                .map(materialUnitDto -> new MaterialUnit()
                        .setProductUnit(materialUnitDto.getProductUnit())
                        .setPurchaseUnit(materialUnitDto.getPurchaseUnit())
                        .setProductPurchaseUnitRatio(materialUnitDto.getProductPurchaseUnitRatio())
                ).toList();
        this.saveBatch(materialUnits);
    }

    private void validationParam(List<MaterialUnitDto> materialUnitDtos) {
        StringBuilder stringBuilder = new StringBuilder();
        for (int i = 0; i < materialUnitDtos.size(); i++) {
            MaterialUnitDto materialUnitDTO = materialUnitDtos.get(i);
            if (!StrUtil.isEmpty(materialUnitDTO.getPurchaseUnit()) && StrUtil.isEmpty(materialUnitDTO.getProductPurchaseUnitRatio())) {
                stringBuilder.append(StrUtil.format("第{}行，生产单位:采购单位不能为空;", i + 1));
            }
        }
        for (int i = 0; i < materialUnitDtos.size(); i++) {
            MaterialUnitDto materialUnitDTO = materialUnitDtos.get(i);
            for (int j = i+1; j < materialUnitDtos.size(); j++) {
                if(EqualsBuilder.reflectionEquals(materialUnitDtos.get(i), materialUnitDtos.get(j),"id")){
                    stringBuilder.append(StrUtil.format("生产单位【{}】已重复;",  materialUnitDTO.getProductUnit()));
                }
            }
        }

        LambdaQueryChainWrapper<MaterialUnit> qw = this.lambdaQuery();
        if (materialUnitDtos.size() > 1) {
            for (int i = 0; i < materialUnitDtos.size() - 1; i++) {
                qw.eq(MaterialUnit::getProductUnit, materialUnitDtos.get(i).getProductUnit());
                setDynamicQueryCondition(materialUnitDtos.get(i), qw);
            }
            qw.or();
        }
        qw.eq(MaterialUnit::getProductUnit, materialUnitDtos.get(materialUnitDtos.size() - 1).getProductUnit());
        setDynamicQueryCondition(materialUnitDtos.get(materialUnitDtos.size() - 1), qw);
        Map<String, MaterialUnit> materialUnitMap = qw.select(MaterialUnit::getProductUnit, MaterialUnit::getPurchaseUnit, MaterialUnit::getProductPurchaseUnitRatio)
                .list().stream().collect(
                        Collectors.toMap(k -> k.getProductUnit() + k.getPurchaseUnit() + k.getProductPurchaseUnitRatio(), a -> a, (a, b) -> a));
        if (materialUnitMap.isEmpty() && stringBuilder.isEmpty()) {
            return;
        }
        for (int i = 0; i < materialUnitDtos.size(); i++) {
            MaterialUnitDto materialUnitDTO = materialUnitDtos.get(i);
            if (materialUnitMap.get(materialUnitDTO.getProductUnit() + materialUnitDTO.getPurchaseUnit() + materialUnitDTO.getProductPurchaseUnitRatio()) != null) {
                stringBuilder.append(StrUtil.format("第{}行，生产单位【{}】系统已存在;", i + 1, materialUnitDTO.getProductUnit()));
//                    stringBuilder.append(StrUtil.format("第{}行，生产单位【{}】，采购单位【{}】，生产单位:采购单位【{}】，系统已存在", i + 1, materialUnitDTO.getProductUnit(), materialUnitDTO.getPurchaseUnit(), materialUnitDTO.getProductPurchaseUnitRatio()));
            }
        }
        throw new GlobalException(stringBuilder.toString());
    }

    /**
     * 更新物料单位
     *
     * @param materialUnitDto 物料单位dto
     * @return 是否更新成功
     */
    @Override
    @Redisson(name = RedisConstants.MATERIAL_UNIT_LOCK_KEY, waitTime = 1)
    public Boolean updateMaterialUnit(MaterialUnitDto materialUnitDto) {
        Integer id = materialUnitDto.getId();
        MaterialUnit materialUnit = this.getById(id);
        if (materialUnit == null) {
            throw SystemCode.PARAM_VALID_ERROR.msgEx("物料单位不存在");
        }
        if (!StrUtil.isEmpty(materialUnitDto.getPurchaseUnit()) && StrUtil.isEmpty(materialUnitDto.getProductPurchaseUnitRatio())) {
            throw SystemCode.PARAM_VALID_ERROR.msgEx("生产单位:采购单位不能为空");
        }
        LambdaQueryChainWrapper<MaterialUnit> qw = this.lambdaQuery()
                .ne(MaterialUnit::getId, id)
                .eq(MaterialUnit::getProductUnit, materialUnitDto.getProductUnit());
        setDynamicQueryCondition(materialUnitDto, qw);
        boolean exists = qw.exists();
        if (exists) {
            if (!StrUtil.isEmpty(materialUnitDto.getPurchaseUnit())) {
                throw new GlobalException(StrUtil.format("生产单位【{}】，采购单位【{}】，生产单位:采购单位【{}】，系统已存在", materialUnitDto.getProductUnit(), materialUnitDto.getPurchaseUnit(), materialUnitDto.getProductPurchaseUnitRatio()));
            } else {
                throw new GlobalException(StrUtil.format("生产单位【{}】，系统已存在", materialUnitDto.getProductUnit()));
            }
        }
        materialUnit.setProductUnit(materialUnitDto.getProductUnit())
                .setPurchaseUnit(materialUnitDto.getPurchaseUnit())
                .setProductPurchaseUnitRatio(materialUnitDto.getProductPurchaseUnitRatio());
        return this.lambdaUpdate()
                .eq(MaterialUnit::getId, id)
                .set(MaterialUnit::getProductUnit, materialUnitDto.getProductUnit())
                .set(MaterialUnit::getPurchaseUnit, materialUnitDto.getPurchaseUnit())
                .set(MaterialUnit::getProductPurchaseUnitRatio, materialUnitDto.getProductPurchaseUnitRatio())
                .update();
    }

    private void setDynamicQueryCondition(MaterialUnitDto materialUnitDto, LambdaQueryChainWrapper<MaterialUnit> qw) {
        if (!StrUtil.isEmpty(materialUnitDto.getPurchaseUnit())) {
            qw.eq(MaterialUnit::getPurchaseUnit, materialUnitDto.getPurchaseUnit());
            qw.eq(MaterialUnit::getProductPurchaseUnitRatio, materialUnitDto.getProductPurchaseUnitRatio());
        } else {
            qw.isNull(MaterialUnit::getPurchaseUnit);
            if (!StrUtil.isEmpty(materialUnitDto.getProductPurchaseUnitRatio())) {
                qw.eq(MaterialUnit::getProductPurchaseUnitRatio, materialUnitDto.getProductPurchaseUnitRatio());
            }else {
                qw.isNull(MaterialUnit::getProductPurchaseUnitRatio);
            }
        }
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    @Redisson(name = RedisConstants.MATERIAL_UNIT_LOCK_KEY, waitTime = 1)
    public void deleteMaterialUnit(List<Long> ids) {
        List<Material> materials = materialService.lambdaQuery()
                .select(Material::getMaterialUnitId)
                .in(Material::getMaterialUnitId, ids)
                .list();
        if (CollUtil.isNotEmpty(materials)) {
            Set<Integer> materialUnitIds = materials.stream().map(Material::getMaterialUnitId).collect(Collectors.toSet());
            this.lambdaQuery()
                    .select(MaterialUnit::getProductUnit)
                    .in(MaterialUnit::getId, materialUnitIds)
                    .list().stream()
                    .map(MaterialUnit::getProductUnit)
                    .reduce((a, b) -> a + "," + b)
                    .ifPresent(productUnit -> {
                                throw new GlobalException(productUnit + "物料单位下存在物料，请先删除物料");
                            }
                    );
        }
        List<MaterialUnit> materialUnits = this.lambdaQuery()
                .select(MaterialUnit::getProductUnit, MaterialUnit::getPurchaseUnit, MaterialUnit::getProductPurchaseUnitRatio)
                .in(MaterialUnit::getId, ids)
                .list();
        this.removeByIds(ids);
    }
}

